﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using A2DFramework.InstanceService;
using A2DFramework.Repository;
using A2DFramework.SQLDispatcherService.Configuration;
using A2DFramework.SQLDispatcherService.Context;

namespace A2DFramework.SQLDispatcherService.SimpleEFDispatcher
{
    public class IntelligentEFRepository : IRepository
    {
        internal DbContext ctx = ObjectFactory.Resolve<DbContext>();

        public virtual IEnumerable<T> Get<T>(
            Expression<Func<T, bool>> filter = null,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            string includeProperties = "")
            where T : class
        {
            ResetReadableDatabaseConnection();
            IQueryable<T> query = ctx.Set<T>();

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

        public virtual T GetByID<T>(object id)
            where T : class
        {
            ResetReadableDatabaseConnection();
            return ctx.Set<T>().Find(id);
        }

        public virtual void Insert<T>(T entity)
            where T : class
        {
            SetWritableDatabaseConnection();
            ctx.Set<T>().Add(entity);
        }

        public virtual void Delete<T>(object id)
            where T : class
        {
            ResetReadableDatabaseConnection();
            T entityToDelete = ctx.Set<T>().Find(id);

            Delete(entityToDelete);
        }

        public virtual void Delete<T>(T entityToDelete)
            where T : class
        {
            SetWritableDatabaseConnection();

            if (ctx.Entry(entityToDelete).State == EntityState.Detached)
            {
                ctx.Set<T>().Attach(entityToDelete);
            }
            ctx.Set<T>().Remove(entityToDelete);
        }

        public virtual void Update<T>(T entityToUpdate)
            where T : class
        {
            SetWritableDatabaseConnection();

            ctx.Set<T>().Attach(entityToUpdate);
            ctx.Entry(entityToUpdate).State = EntityState.Modified;
        }

        public virtual IEnumerable<T> GetWithRawSql<T>(string query, params object[] parameters)
            where T : class
        {
            ResetReadableDatabaseConnection();

            return ctx.Set<T>().SqlQuery(query, parameters).ToList();
        }


        public void Save()
        {
            SetWritableDatabaseConnection();

            ctx.SaveChanges();
        }


        private void SetWritableDatabaseConnection()
        {
            string cs = Configuration.SQLDispatcherConfiguration.WritableDB.ConnectionString;
            ctx.Database.Connection.ConnectionString = cs;
        }
        private void ResetReadableDatabaseConnection()
        {
            string region = SQLDispatcherContext.GetCurrentContext().Region;

            string cs=GetReadableConnectionString(region);

            ctx.Database.Connection.ConnectionString = cs;
        }

        private static string GetReadableConnectionString(string region)
        {

            if (region == null || region.Length == 0)       //from normal read dbs
            {
                int random = GenerateRandomNumber();
                int dbIndex = random % Configuration.SQLDispatcherConfiguration.ReadDBs.Count;
                return Configuration.SQLDispatcherConfiguration.ReadDBs[dbIndex].ConnectionString;
            }

            DedicatedRegion r = Configuration.SQLDispatcherConfiguration.DedicatedRegions.Find(t => t.Region.Equals(region, StringComparison.OrdinalIgnoreCase));
            if (r == null)
                throw new Exception("No such Dedicated Region Identifier.");

            {
                int random = GenerateRandomNumber();
                int dbIndex = random % r.DBs.Count;
                return r.DBs[dbIndex].ConnectionString;
            }
        }

        private static int GenerateRandomNumber()
        {
            Random Random1 = new Random();
            //产生0到1000的随机数
            int i1 = Random1.Next(0, 1001);
            return i1;
        }
    }
}
